#include "sidecar.h"
#include "lib_sidecar.hpp"

#include <cstring>

Sidecar::Sidecar(redisContext* redis_ctx) : _redis_ctx(redis_ctx) {
}

Sidecar::Sidecar() {

};

Sidecar::~Sidecar() {
    if (this->_redis_ctx != NULL) {
        redisFree(this->_redis_ctx);
    }
}

Sidecar* Sidecar::init(const char* ip_addr, int port) {
    redisContext* redisCtx = redisConnect(ip_addr, port);
    if (redisCtx != NULL && redisCtx->err) {
        printf("error: %s\n", redisCtx->errstr);
        return NULL;
    } else {
        return new Sidecar(redisCtx);
    }
}

Sidecar* Sidecar::reconnect(Sidecar* sidecar, const char* ip_addr, int port) {
    redisContext* redisCtx = redisConnect(ip_addr, port);
    if (redisCtx != NULL && redisCtx->err) {
        printf("error: %s\n", redisCtx->errstr);
        delete sidecar;
        return NULL;
    } else {
        delete sidecar;
        return new Sidecar(redisCtx);
    }
}

void Sidecar::destroy(Sidecar* sidecar) {
    delete sidecar;
}

int Sidecar::set_int_value(const char* url, const char* metrics, long long value) {

    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "SET %s %lld", url_with_metrics.c_str(), value);
    freeReplyObject(reply);

    return 0;
}

int Sidecar::incr_int_value(const char* url, const char* metrics) {
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "INCR %s", url_with_metrics.c_str());
    freeReplyObject(reply);

    return 0;
}

int Sidecar::decr_int_value(const char* url, const char* metrics) {
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "DECR %s", url_with_metrics.c_str());
    freeReplyObject(reply);

    return 0;
}

int Sidecar::add_int_value(const char* url, const char* metrics, long long value) {
    long long v = this->get_int_value(url, metrics);
    return this->set_int_value(url, metrics, value + v);
}

int Sidecar::set_float_value(const char* url, const char* metrics, double value) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "SET %s %f", url_with_metrics.c_str(), value);
    freeReplyObject(reply);

    return 0;
}

int Sidecar::set_str_value(const char* url, const char* metrics, const char* value, size_t value_len) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    std::string str(value, value_len);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "SET %s %s", url_with_metrics.c_str(), str.c_str());
    freeReplyObject(reply);
    
    return 0;
}

int Sidecar::set_struct_value(const char* url, const char* metrics, const char* value, size_t value_len) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "SET %b %b", url_with_metrics.c_str(), (size_t) strlen(url_with_metrics.c_str()), value, value_len);
    freeReplyObject(reply);
    return 0;
}

long long Sidecar::get_int_value(const char* url, const char* metrics) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "GET %s", url_with_metrics.c_str());
    printf("%s\n", url_with_metrics.c_str());

    long long v = atoll(reply->str);

    freeReplyObject(reply);

    return v;
}

double Sidecar::get_float_value(const char* url, const char* metrics) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "GET %s", url_with_metrics.c_str());

    double v = atof(reply->str);

    freeReplyObject(reply);

    return v;
}

int Sidecar::get_str_value(const char* url, const char* metrics, char* str, size_t* str_len) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "GET %s", url_with_metrics.c_str());

    *str_len = reply->len;
    strncpy(str, reply->str, reply->len);

    freeReplyObject(reply);

    return 0;
}

int Sidecar::get_struct_value(const char* url, const char* metrics, char* value, size_t* value_len) {
    
    std::string url_with_metrics(url);
    url_with_metrics.append(".");
    url_with_metrics.append(metrics);

    redisReply* reply = (redisReply*) redisCommand(this->_redis_ctx, "GET %s", url_with_metrics.c_str());
    *value_len = reply->len;

    memcpy(value, reply->str, reply->len);
    freeReplyObject(reply);

    return 0;
}

#ifdef __cplusplus
extern "C" {
#endif

long long get_int_value(const char* url, const char* metrics) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->get_int_value(url, metrics);
    Sidecar::destroy(sidecar);
    return ret;
}

double get_float_value(const char* url, const char* metrics) {
    Sidecar* sidecar = Sidecar::init();
    double ret = sidecar->get_float_value(url, metrics);
    Sidecar::destroy(sidecar);
    return ret;
}

void get_str_value(const char* url, const char* metrics, char* str, size_t* str_len) {
    Sidecar* sidecar = Sidecar::init();
    sidecar->get_str_value(url, metrics, str, str_len);
    Sidecar::destroy(sidecar);
    return;
}

void get_struct_value(const char* url, const char* metrics, char* value, size_t* value_len) {
    Sidecar* sidecar = Sidecar::init();
    sidecar->get_struct_value(url, metrics, value, value_len);
    Sidecar::destroy(sidecar);
    return;
}

int set_int_value(const char* url, const char* metrics, long long value) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->set_int_value(url, metrics, value);
    Sidecar::destroy(sidecar);
    return ret;
}

int incr_int_value(const char* url, const char* metrics) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->incr_int_value(url, metrics);
    Sidecar::destroy(sidecar);
    return ret;
}

int decr_int_value(const char* url, const char* metrics) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->decr_int_value(url, metrics);
    Sidecar::destroy(sidecar);
    return ret;
}

int add_int_value(const char* url, const char* metrics, long long value) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->add_int_value(url, metrics, value);
    Sidecar::destroy(sidecar);
    return ret;
}

int set_float_value(const char* url, const char* metrics, double value) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->set_float_value(url, metrics, value);
    Sidecar::destroy(sidecar);
    return ret;
}

int set_str_value(const char* url, const char* metrics, const char* str, size_t str_len) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->set_str_value(url, metrics, str, str_len);
    Sidecar::destroy(sidecar);
    return ret;
}

int set_struct_value(const char* url, const char* metrics, char* value, size_t value_len) {
    Sidecar* sidecar = Sidecar::init();
    int ret = sidecar->set_struct_value(url, metrics, value, value_len);
    Sidecar::destroy(sidecar);
    return ret;
}

#ifdef __cplusplus
};
#endif
